﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Xml;
using HtmlAgilityPack;
using System.Xml.Serialization;
using System.Diagnostics;

namespace GeocacheDownloader
{
    #region Marker
    public struct Marker
    {
        string _text;

        public string Text
        {
            get { return _text; }
            set { _text = value; }
        }
        string _lat, _lon;

        public string Lon
        {
            get { return _lon; }
            set { _lon = value; }
        }

        public string Lat
        {
            get { return _lat; }
            set { _lat = value; }
        }
        Color _color;

        public Color Color
        {
            get { return _color; }
            set { _color = value; }
        }

        public Marker(string text, string latitude, string longitude, Color markerColor)
        {
            _text = text;
            _lat = latitude;
            _lon = longitude;
            _color = markerColor;
        }

        public override string ToString()
        {
            return Text + ";" + Lat + ";" + Lon + ";" + Color.ToString();
        }
    }
    #endregion

    public partial class MainForm : Form
    {
        private string homeDir = Application.StartupPath;//@"C:\Documents and Settings\MutiuPa\My Documents\Geocaching";
        private const string IMAGE_WEB = "http://maps.google.com/maps/api/staticmap?";
        private const string CACHE_WEB = "http://www.geocaching.com/seek/cache_details.aspx?wp=";
        private const string AND = "&";
        private const string EQUALS = "=";
        private const string SEPARATOR = "|";
        private const string MARKER_SEPARATOR = ":";
        private const string DESCRIPTION = "description.txt";
        private const string LOCFILE = "@.loc";
        private const string IMAGE = "img@.jpg";
        private string imageFileName = string.Empty;
        private int counter = 0;
        private List<Cache> codes = new List<Cache>();
        private bool isKMLAnalysed = false;
        private Dictionary<int, string> cacheTypes = new Dictionary<int, string>();
        private ErrorLogWriter err;
        public MainForm()
        {
            InitializeComponent();
            LoadLists();
            DefaultValues();
        }
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);
            //// Close error file
            //err.Close();
        }
        private void LoadLists()
        {
            foreach (string s in Enum.GetNames(typeof(Helper.MapType)))
            {
                cmbType.Items.Add(s);
            }
            foreach (string s in Enum.GetNames(typeof(Helper.ImageFormat)))
            {
                cmbFormat.Items.Add(s);
            }
            for (int i = 19; i > 0; i--)
            {
                cmbZoom.Items.Add(i.ToString());
            }

            cmbSize.Items.Add(new Point(480, 640));
            cmbSize.Items.Add(new Point(400, 400));
        }

        private void DefaultValues()
        {
            cmbColor.SelectedItem = "red";
            btnRemoveImage.Enabled = lstImages.Items.Count > 0;
            txtKMLPath.Text = Application.StartupPath + txtKMLPath.Text.Substring(txtKMLPath.Text.LastIndexOf("\\"));
            txtPath.Text = Application.StartupPath;
        }

        private byte[] downloadedData;

        //Connects to a URL and attempts to download the file
        private void downloadData(string url)
        {
            progressCache.Value = 0;

            downloadedData = new byte[0];
            try
            {
                //Optional
                this.Text = "Connecting...";
                Application.DoEvents();

                //Get a data stream from the url
                WebRequest req = WebRequest.Create(url);
                WebResponse response = req.GetResponse();
                Stream stream = response.GetResponseStream();

                //Download in chuncks
                byte[] buffer = new byte[1024];

                //Get Total Size
                int dataLength = (int)response.ContentLength;

                //With the total data we can set up our progress indicators
                progressCache.Maximum = dataLength;
                lblProgress.Text = "0/" + dataLength.ToString();

                this.Text = "Downloading...";
                Application.DoEvents();

                //Download to memory
                //Note: adjust the streams here to download directly to the hard drive
                MemoryStream memStream = new MemoryStream();
                while (true)
                {
                    //Try to read the data
                    int bytesRead = stream.Read(buffer, 0, buffer.Length);

                    if (bytesRead == 0)
                    {
                        //Finished downloading
                        progressCache.Value = progressCache.Maximum;
                        lblProgress.Text = dataLength.ToString() + "/" + dataLength.ToString();

                        Application.DoEvents();
                        break;
                    }
                    else
                    {
                        //Write the downloaded data
                        memStream.Write(buffer, 0, bytesRead);

                        //Update the progress bar
                        if (progressCache.Value + bytesRead <= progressCache.Maximum)
                        {
                            progressCache.Value += bytesRead;
                            lblProgress.Text = progressCache.Value.ToString() + "/" + dataLength.ToString();

                            progressCache.Refresh();
                            Application.DoEvents();
                        }
                    }
                }

                //Convert the downloaded stream to a byte array
                downloadedData = memStream.ToArray();

                //Clean up
                stream.Close();
                memStream.Close();
            }
            catch (Exception ex)
            {
                //May not be connected to the internet
                //Or the URL might not exist
                //MessageBox.Show("There was an error accessing the URL.");
                //Console.Error.WriteLine(url);
                err.WriteLine(url, txtName.Text);
                LogMessageToFile(url);//"Downloading data error: URL:" + url + "\r\nError message:" + ex.Message);
            }
            this.Text = "Download Data through HTTP";
        }

        private void CreateCacheDirectory(string path, string directoryName)
        {
            if (!Directory.Exists(Path.Combine(path, directoryName)))
                Directory.CreateDirectory(Path.Combine(path, directoryName));
        }

        private void CreateFile(string cacheDir, string file, string body)
        {
            if (file.Length > 255)
                file = file.Substring(0, 255);
            File.WriteAllText(Path.Combine(Path.Combine(homeDir, cacheDir), file), body);
        }


        private string NextImage(string image)
        {
            counter++;
            return image.Replace("@", counter.ToString());
        }

        private void CreateImages(string link, string dirName, string filename)
        {
            downloadData(link);
            SaveImage(link, dirName, filename);
            //if (downloadedData != null && downloadedData.Length != 0)
            //{
            //    this.Text = "Saving Data...";
            //    Application.DoEvents();
            //    string path = Path.Combine(homeDir, Path.Combine(dirName, filename));
            //    if (path.Length > 260)
            //        path = path.Substring(0, 255) + path.Substring(path.LastIndexOf("."));
            //    //Write the bytes to a file
            //    FileStream newFile = new FileStream(path, FileMode.Create);
            //    newFile.Write(downloadedData, 0, downloadedData.Length);
            //    newFile.Close();

            //    this.Text = "Download Data";
            //    Debug.WriteLine("Saved Successfully");
            //}
            //else
            //    Debug.WriteLine("No File was Downloaded Yet!");
        }

        private void SaveImage(string link, string dirName, string filename)
        {
            if (downloadedData != null && downloadedData.Length != 0)
            {
                this.Text = "Saving Data...";
                Application.DoEvents();
                string path = Path.Combine(homeDir, Path.Combine(dirName, filename));
                if (path.Length > 260)
                    path = path.Substring(0, 255) + path.Substring(path.LastIndexOf("."));
                //Write the bytes to a file
                FileStream newFile = new FileStream(path, FileMode.Create);
                newFile.Write(downloadedData, 0, downloadedData.Length);
                newFile.Close();

                this.Text = "Download Data";
                Debug.WriteLine("Saved Successfully");
            }
            else
                Debug.WriteLine("No File was Downloaded Yet!");
        }

        private string DecryptHint(string encryptedHint)
        {
            //  Decryption Key
            //  A|B|C|D|E|F|G|H|I|J|K|L|M
            //  -------------------------
            //  N|O|P|Q|R|S|T|U|V|W|X|Y|Z
            string result = string.Empty;
            List<char> word = new List<char>();
            foreach (char chr in encryptedHint)
            {
                bool upper = char.IsUpper(chr);
                char tmp = ' ';
                #region dictionary
                switch (char.ToUpper(chr))
                {
                    case 'A':
                        tmp = 'N';
                        break;
                    case 'B':
                        tmp = 'O';
                        break;
                    case 'C':
                        tmp = 'P';
                        break;
                    case 'D':
                        tmp = 'Q';
                        break;
                    case 'E':
                        tmp = 'R';
                        break;
                    case 'F':
                        tmp = 'S';
                        break;
                    case 'G':
                        tmp = 'T';
                        break;
                    case 'H':
                        tmp = 'U';
                        break;
                    case 'I':
                        tmp = 'V';
                        break;
                    case 'J':
                        tmp = 'W';
                        break;
                    case 'K':
                        tmp = 'X';
                        break;
                    case 'L':
                        tmp = 'Y';
                        break;
                    case 'M':
                        tmp = 'Z';
                        break;
                    case 'N':
                        tmp = 'A';
                        break;
                    case 'O':
                        tmp = 'B';
                        break;
                    case 'P':
                        tmp = 'C';
                        break;
                    case 'Q':
                        tmp = 'D';
                        break;
                    case 'R':
                        tmp = 'E';
                        break;
                    case 'S':
                        tmp = 'F';
                        break;
                    case 'T':
                        tmp = 'G';
                        break;
                    case 'U':
                        tmp = 'H';
                        break;
                    case 'V':
                        tmp = 'I';
                        break;
                    case 'W':
                        tmp = 'J';
                        break;
                    case 'X':
                        tmp = 'K';
                        break;
                    case 'Y':
                        tmp = 'L';
                        break;
                    case 'Z':
                        tmp = 'M';
                        break;
                    default:
                        break;
                }
                #endregion
                word.Add(upper ? char.ToUpper(tmp) : char.ToLower(tmp));
            }
            return new string(word.ToArray());
        }

        string locDescription = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<loc version=""1.0"" src=""Groundspeak"">
@waypoint@
</loc>";
        string locBody = string.Empty;
        private void CreateLOC(Cache c)
        {
            string body = @"<waypoint>
	<name id=""@@code@@""><![CDATA[@@name@@]]></name>
	<coord lat=""@@lat@@"" lon=""@@lon@@""/>
	<type>Geocache</type>
	<link text=""Cache Details"">http://www.geocaching.com/seek/cache_details.aspx?wp=@@code@@</link>
</waypoint>";
            body = body.Replace("@@code@@", c.Code).Replace("@@name@@", c.Name).Replace("@@lat@@", c.Latitude).Replace("@@lon@@", c.Longitude);
            locBody += body;
            //CreateFile(c.Code, LOCFILE.Replace("@", c.Code), body);

            //// Create file to save the data to
            //FileStream fs = new FileStream(Path.Combine(Path.Combine(homeDir, c.Code), LOCFILE.Replace("@", c.Code)), FileMode.Create);
            //// Create an XmlSerializer object to perform the serialization
            //XmlSerializer xs = new XmlSerializer(typeof(DateTime));
            //// Use the XmlSerializer object to serialize the data to the file
            //xs.Serialize(fs, System.DateTime.Now);
            //// Close the file
            //fs.Close();
        }

        private void ParseKML(string KMLpath)
        {
            Cursor old = Cursor.Current;
            try
            {
                Cursor.Current = Cursors.WaitCursor;
                XmlDocument doc = new XmlDocument();
                cacheTypes.Clear();
                doc.Load(KMLpath);
                XmlNamespaceManager xnms = new XmlNamespaceManager(doc.NameTable);
                xnms.AddNamespace("kml", "http://www.opengis.net/kml/2.2");
                foreach (XmlNode node1 in doc.SelectNodes("//kml:Folder", xnms))
                {
                    foreach (XmlNode node2 in node1.ChildNodes)
                        foreach (XmlNode node in node2.ChildNodes)
                        {
                            #region cache types
                            ListViewItem lwi = null;
                            if (node.Name.ToLower() == "name")
                            {
                                if (node.InnerText.StartsWith(Helper.CT_CITO))
                                {
                                    lwi = new ListViewItem(Helper.CT_CITO);
                                    lwi.Name = Helper.CT_CITO;
                                    lwi.SubItems.Add(GetNoOfCaches(node.InnerText));
                                    lwi.ImageIndex = 0;
                                }
                                else if (node.InnerText.StartsWith(Helper.CT_Earth))
                                {
                                    lwi = new ListViewItem(Helper.CT_Earth);
                                    lwi.Name = Helper.CT_Earth;
                                    lwi.SubItems.Add(GetNoOfCaches(node.InnerText));
                                    lwi.ImageIndex = 1;
                                }
                                else if (node.InnerText.StartsWith(Helper.CT_Event))
                                {
                                    lwi = new ListViewItem(Helper.CT_Event);
                                    lwi.Name = Helper.CT_Event;
                                    lwi.SubItems.Add(GetNoOfCaches(node.InnerText));
                                    lwi.ImageIndex = 2;
                                }
                                else if (node.InnerText.StartsWith(Helper.CT_Locationless))
                                {
                                    lwi = new ListViewItem(Helper.CT_Locationless);
                                    lwi.Name = Helper.CT_Locationless;
                                    lwi.SubItems.Add(GetNoOfCaches(node.InnerText));
                                    lwi.ImageIndex = 3;
                                }
                                else if (node.InnerText.StartsWith(Helper.CT_MegaEvent))
                                {
                                    lwi = new ListViewItem(Helper.CT_MegaEvent);
                                    lwi.Name = Helper.CT_MegaEvent;
                                    lwi.SubItems.Add(GetNoOfCaches(node.InnerText));
                                    lwi.ImageIndex = 4;
                                }
                                else if (node.InnerText.StartsWith(Helper.CT_Multi))
                                {
                                    lwi = new ListViewItem(Helper.CT_Multi);
                                    lwi.Name = Helper.CT_Multi;
                                    lwi.SubItems.Add(GetNoOfCaches(node.InnerText));
                                    lwi.ImageIndex = 5;
                                }
                                else if (node.InnerText.StartsWith(Helper.CT_Mystery))
                                {
                                    lwi = new ListViewItem(Helper.CT_Mystery);
                                    lwi.Name = Helper.CT_Mystery;
                                    lwi.SubItems.Add(GetNoOfCaches(node.InnerText));
                                    lwi.ImageIndex = 6;
                                }
                                else if (node.InnerText.StartsWith(Helper.CT_Traditional))
                                {
                                    lwi = new ListViewItem(Helper.CT_Traditional);
                                    lwi.Name = Helper.CT_Traditional;
                                    lwi.SubItems.Add(GetNoOfCaches(node.InnerText));
                                    lwi.ImageIndex = 7;
                                }
                                else if (node.InnerText.StartsWith(Helper.CT_Virtual))
                                {
                                    lwi = new ListViewItem(Helper.CT_Virtual);
                                    lwi.Name = Helper.CT_Virtual;
                                    lwi.SubItems.Add(GetNoOfCaches(node.InnerText));
                                    lwi.ImageIndex = 8;
                                }
                                else if (node.InnerText.StartsWith(Helper.CT_Webcam))
                                {
                                    lwi = new ListViewItem(Helper.CT_Webcam);
                                    lwi.Name = Helper.CT_Webcam;
                                    lwi.SubItems.Add(GetNoOfCaches(node.InnerText));
                                    lwi.ImageIndex = 9;
                                }
                                else if (node.InnerText.StartsWith(Helper.CT_Wherigo))
                                {
                                    lwi = new ListViewItem(Helper.CT_Wherigo);
                                    lwi.Name = Helper.CT_Wherigo;
                                    lwi.SubItems.Add(GetNoOfCaches(node.InnerText));
                                    lwi.ImageIndex = 10;
                                }
                                if (lwi != null)
                                    lstCacheTypes.Items.Add(lwi);
                            }
                            #endregion
                            if (node.Name == "Placemark")
                            {
                                Cache c = new Cache();
                                foreach (XmlNode n in node.ChildNodes)
                                {
                                    switch (n.Name)
                                    {
                                        case "ExtendedData":
                                            foreach (XmlNode tmp in n.ChildNodes)
                                            {
                                                switch (tmp.Attributes["name"].Value)
                                                {
                                                    case "gc_code":
                                                        c.Code = tmp.InnerText;
                                                        break;
                                                    case "gc_name":
                                                        c.Name = tmp.InnerText;
                                                        break;
                                                    case "gc_cacheType":
                                                        c.CacheType = tmp.InnerText;
                                                        break;
                                                    default:
                                                        break;
                                                }
                                            }
                                            break;
                                        case "Point":
                                            string[] coordinates = n.InnerText.Split(new char[] { ',' });
                                            c.Longitude = coordinates[0];
                                            c.Latitude = coordinates[1];
                                            break;
                                        default:
                                            break;
                                    }
                                }
                                if (!c.IsEmpty())
                                    codes.Add(c);
                            }
                        }
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine(ex.Message);
            }
            finally { Cursor.Current = old; }
        }

        private string GetNoOfCaches(string stringToParse)
        {
            string tmp = stringToParse.Substring(stringToParse.IndexOf("[") + 1);
            tmp = tmp.Substring(0, tmp.Length - 1);
            return tmp;
        }

        int nextCounter = 0;
        private string GetNextCounter()
        {
            nextCounter++;
            return nextCounter.ToString().PadLeft(4, '0');
        }

        public void GetGoogleMapImage(string directory, Marker coordinates, bool marker, int zoom, Point size, Helper.MapType maptype, Helper.ImageFormat imageFormat, bool sensor, bool mobile, params Marker[] additionalMarkers)
        {/*
http://maps.google.com/maps/api/staticmap?center=Brooklyn+Bridge,New+York,NY&zoom=14&size=512x512&maptype=roadmap
&markers=color:blue|label:S|40.702147,-74.015794&markers=color:green|label:G|40.711614,-74.012318
&markers=color:red|color:red|label:C|40.718217,-73.998284&sensor=false
            */
            //extension
            string ext = string.Empty;
            switch (imageFormat)
            {
                case Helper.ImageFormat.png:
                case Helper.ImageFormat.png32:
                    ext = "png";
                    break;
                case Helper.ImageFormat.gif:
                    ext = "gif";
                    break;
                case Helper.ImageFormat.jpg:
                case Helper.ImageFormat.jpg_baseline:
                default:
                    ext = "jpg";
                    break;
            }
            string filename = maptype.ToString() + "_" + zoom.ToString() + "." + ext;
            string url = IMAGE_WEB;
            #region parameters
            //center coordinates
            url += Helper.GMapsParameters.center + EQUALS + coordinates.Lat + "," + coordinates.Lon;
            //zoom
            url += AND + Helper.GMapsParameters.zoom + EQUALS + zoom.ToString();
            //size
            url += AND + Helper.GMapsParameters.size + EQUALS + size.X + "x" + size.Y;
            //TODO
            //path
            //visible
            //map type
            url += AND + Helper.GMapsParameters.maptype + EQUALS + maptype.ToString();
            //marker on the center
            if (marker)
            {
                url += AND + Helper.GMapsParameters.markers + EQUALS + Helper.GMapsParameters.color + MARKER_SEPARATOR + coordinates.Color.Name.ToLower() +
                    SEPARATOR + Helper.GMapsParameters.label + MARKER_SEPARATOR + coordinates.Text + SEPARATOR + coordinates.Lat + "," + coordinates.Lon;
            }
            //other markers
            foreach (Marker m in additionalMarkers)
            {
                url += AND + Helper.GMapsParameters.markers + EQUALS + Helper.GMapsParameters.color + MARKER_SEPARATOR + coordinates.Color.Name.ToLower() +
                    SEPARATOR + Helper.GMapsParameters.label + MARKER_SEPARATOR + coordinates.Text + SEPARATOR + coordinates.Lat + "," + coordinates.Lon;
            }
            //mobile
            url += AND + Helper.GMapsParameters.mobile + EQUALS + mobile.ToString().ToLower();
            //sensor
            url += AND + Helper.GMapsParameters.sensor + EQUALS + sensor.ToString().ToLower();
            #endregion
            downloadData(url);
            SaveImage(url, directory, filename);
            //if (downloadedData != null && downloadedData.Length != 0)
            //{
            //    this.Text = "Saving Data...";
            //    Application.DoEvents();

            //    //Write the bytes to a file
            //    FileStream newFile = new FileStream(Path.Combine(directory, filename), FileMode.Create);
            //    newFile.Write(downloadedData, 0, downloadedData.Length);
            //    //result = Image.FromStream(newFile);
            //    newFile.Close();

            //    this.Text = "Download Data";
            //    // MessageBox.Show("Saved Successfully");
            //}
            // MessageBox.Show("No File was Downloaded Yet!");
        }

        private void btnGo_Click(object sender, EventArgs e)
        {
            //log
            err = new ErrorLogWriter(Directory.GetCurrentDirectory() + @"\Error.xml");
            Console.SetError(err);

            groupBox4.Enabled = false;
            homeDir = txtPath.Text;
            if (!isKMLAnalysed)
                try
                {
                    ParseKML(txtKMLPath.Text);
                    isKMLAnalysed = true;
                    foreach (ListViewItem item in lstCacheTypes.Items)
                    {
                        item.Checked = true;
                    }
                }
                catch { isKMLAnalysed = false; }
            HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
            string x = string.Empty;
            progressGlobal.Value = 0;
            progressGlobal.Maximum = codes.Count;
            Cursor.Current = Cursors.WaitCursor;
            try
            {
                foreach (Cache cache in codes)
                {
                    if (!lstCacheTypes.Items[cache.CacheType].Checked)
                        continue;
                    try
                    {
                        Cursor = Cursors.WaitCursor;
                        var req = (HttpWebRequest)HttpWebRequest.Create(CACHE_WEB + cache.Code);
                        using (var resp = req.GetResponse().GetResponseStream())
                        using (var read = new StreamReader(resp))
                        {
                            var txt = read.ReadToEnd();
                            x = txt;
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.Error.WriteLine(ex.Message);
                        //MessageBox.Show("Error loading file: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                    }
                    doc.LoadHtml(x);

                    //if (doc.GetElementbyId("ctl00_ContentBody_CacheInformationTable") == null)
                    //{
                    //    progressGlobal.Value++;
                    //    progressGlobal.Refresh();
                    //    continue;
                    //}
                    //code
                    txtName.Text = doc.GetElementbyId("ctl00_ContentBody_CacheName").ChildNodes[0].InnerText.Trim();
                    //short description
                    txtShortDes.Text = doc.GetElementbyId("ctl00_ContentBody_ShortDescription") == null ? string.Empty : doc.GetElementbyId("ctl00_ContentBody_ShortDescription").InnerText;
                    //description
                    txtDescription.Text = doc.GetElementbyId("ctl00_ContentBody_LongDescription").InnerText;
                    //hint text
                    if (doc.GetElementbyId("ctl00_ContentBody_Hints") != null)
                        txtHints.Text = DecryptHint(doc.GetElementbyId("div_hint").InnerText);
                    else
                        txtHints.Text = "No hints available";
                    string dirName = string.Empty;
                    //is cache disabled
                    if (doc.GetElementbyId("ctl00_ContentBody_ErrorText") != null)
                    {
                        if (!chkDisabled.Checked)
                        {
                            progressGlobal.Value++;
                            progressGlobal.Refresh();
                            continue;
                        }
                        dirName = cache.Code + "_DIS";
                    }
                    else
                        dirName = cache.Code;
                    CreateCacheDirectory(homeDir, dirName);
                    string description = txtName.Text + Environment.NewLine + Environment.NewLine + txtShortDes.Text + Environment.NewLine + txtDescription.Text + Environment.NewLine + Environment.NewLine + txtHints.Text;
                    CreateFile(dirName, DESCRIPTION, description);
                    //if the cache is not disabled, add it 
                    if (!dirName.EndsWith("_DIS"))
                        CreateLOC(cache);
                    //images
                    if (doc.GetElementbyId("ctl00_ContentBody_Images") != null)
                    {
                        //there are images
                        XmlDocument imgdoc = new XmlDocument();
                        string html = doc.GetElementbyId("ctl00_ContentBody_Images").InnerHtml;
                        html = html.Replace("</a>", ""); html = html.Replace("<br><br><a", "</a><a"); html = html.Replace("<img src=\"/images/stockholm/16x16/images.gif\">", "");
                        html = html.Replace("<br>", "</text>"); html = html.Replace("</text></text>", "<text>"); html = html.Replace("</text><a", "</text></a><a");
                        if (html.TrimEnd(' ').EndsWith("</text>")) html += "</a>"; if (html.TrimEnd(' ').EndsWith("<text>")) html = html.Substring(0, html.TrimEnd().Length - 6) + "</a>";
                        imgdoc.LoadXml("<tmp/>");
                        imgdoc.DocumentElement.InnerXml = html;
                        //process each image
                        foreach (XmlNode node in imgdoc.SelectNodes("tmp/a"))
                        {
                            string link = (node as XmlElement).GetAttribute("href");
                            string tmpcount = GetNextCounter();
                            string picName = ((node as XmlElement)["span"] == null || string.IsNullOrEmpty((node as XmlElement)["span"].InnerText)) ? "IMG" + tmpcount : RemoveSpecialChars((node as XmlElement)["span"].InnerText);
                            string picDescription = ((node as XmlElement)["text"] == null || string.IsNullOrEmpty((node as XmlElement)["text"].InnerText)) ? "DESC" + tmpcount : RemoveSpecialChars((node as XmlElement)["text"].InnerText);
                            string ext = link.Substring(link.LastIndexOf("."));
                            //create the image
                            CreateImages(link, dirName, picName + "__" + picDescription + ext);
                        }
                    }
                    //maps
                    if (chkMaps.Checked)
                    {
                        foreach (ImageFile file in lstImages.Items)
                        {
                            Marker m;
                            if (file.Marker.HasValue)
                            {
                                m = file.Marker.Value;
                                m.Lat = cache.Latitude;
                                m.Lon = cache.Longitude;
                            }
                            else
                                m = new Marker(txtLabel.Text, cache.Latitude, cache.Longitude, Color.FromName(cmbColor.SelectedItem.ToString()));

                            if (chkMarkers.Checked)
                                GetGoogleMapImage(Path.Combine(homeDir, dirName), m, true, file.Zoom, file.Size, file.Type, file.Format, file.Sensor, file.Mobile);
                            else
                                GetGoogleMapImage(Path.Combine(homeDir, dirName), m, false, file.Zoom, file.Size, file.Type, file.Format, file.Sensor, file.Mobile);

                        }
                        //TODO additional markers
                    }
                    progressGlobal.Value++;
                    progressGlobal.Refresh();
                }
                string locName = txtKMLPath.Text.Substring(txtKMLPath.Text.LastIndexOf("\\") + 1);
                locName = locName.Substring(0, locName.Length - 4);
                CreateFile(string.Empty, LOCFILE.Replace("@", locName), locDescription.Replace("@waypoint@", locBody));
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine(ex.Message);
            }
            finally
            {
                progressGlobal.Refresh();
                // Close error file
                err.Close();
                Cursor.Current = Cursors.Arrow;
                if (File.Exists(homeDir + "\\log.txt"))
                {
                    if (MessageBox.Show("Done with errors. View log file?", "Done", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
                        System.Diagnostics.Process.Start(homeDir + "\\log.txt");
                }
                else
                    MessageBox.Show("Done with no errors.", "Done", MessageBoxButtons.OK, MessageBoxIcon.Information);
                progressCache.Value = 0;
                progressGlobal.Value = 0;
                groupBox4.Enabled = true;
                Cursor.Current = Cursors.Arrow;
                this.Text = "GeocacheDownloader";
            }
        }

        private string RemoveSpecialChars(string str)
        {
            List<string> chars = new List<string>();
            chars.Add("\\");
            chars.Add("/");
            chars.Add(":");
            chars.Add("*");
            chars.Add("?");
            chars.Add("\"");
            chars.Add(">");
            chars.Add("<");
            chars.Add("|");
            foreach (string s in chars)
                if (str.Contains(s))
                    str = str.Replace(s, "_");
            return str;
        }

        private void chkMarkers_CheckedChanged(object sender, EventArgs e)
        {
            grpMarkers.Enabled = chkMarkers.Checked;
        }

        private void chkMaps_CheckedChanged(object sender, EventArgs e)
        {
            grpImages.Enabled = chkMaps.Checked;
        }

        private void btnAdd_Click(object sender, EventArgs e)
        {
            try
            {
                if (!CheckIfCombosSelected())
                {
                    MessageBox.Show("Select values!");
                    return;
                }
                Nullable<Marker> m = null;
                if (chkMarkers.Checked)
                    m = new Marker(txtLabel.Text, string.Empty, string.Empty, Color.FromName(cmbColor.SelectedItem.ToString()));
                ImageFile f = new ImageFile((Helper.MapType)Enum.Parse(typeof(Helper.MapType), cmbType.SelectedItem.ToString()),
                    (Helper.ImageFormat)Enum.Parse(typeof(Helper.ImageFormat), cmbFormat.SelectedItem.ToString()), m,
                    Convert.ToInt16(cmbZoom.SelectedItem), (Point)cmbSize.SelectedItem, chkTestMobile.Checked, chkTestSensor.Checked);
                lstImages.Items.Add(f);
                btnRemoveImage.Enabled = true;
            }
            catch (Exception ex) { MessageBox.Show(ex.Message); }
        }

        private bool CheckIfCombosSelected()
        {
            if (cmbFormat.SelectedItem == null || cmbSize.SelectedItem == null ||
                cmbType.SelectedItem == null || cmbZoom.SelectedItem == null)
                return false;
            return true;
        }

        private void btnRemoveImage_Click(object sender, EventArgs e)
        {
            if (lstImages.SelectedItem != null)
            {
                lstImages.Items.Remove(lstImages.SelectedItem);
                btnRemoveImage.Enabled = lstImages.Items.Count > 0;
            }
        }

        private void btnAnalyze_Click(object sender, EventArgs e)
        {
            if (!isKMLAnalysed)
                try
                {
                    ParseKML(txtKMLPath.Text);
                    isKMLAnalysed = true;
                }
                catch { isKMLAnalysed = false; }
        }

        private void btnBrowseKML_Click(object sender, EventArgs e)
        {
            try
            {
                openKML.CheckPathExists = true;
                openKML.CheckFileExists = true;
                openKML.FileName = txtKMLPath.Text;
                openKML.Filter = "KML files |*.kml";
                openKML.Multiselect = false;
                openKML.Title = "Choose a KML file";
                openKML.ShowDialog();
                txtKMLPath.Text = openKML.FileName;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void btnBrowseOutput_Click(object sender, EventArgs e)
        {
            try
            {
                openOutput.SelectedPath = GetLastExistingDirectory(txtPath.Text);
                openOutput.ShowNewFolderButton = true;
                openOutput.ShowDialog();
                txtPath.Text = openOutput.SelectedPath;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private string GetLastExistingDirectory(string path)
        {
            if (Directory.Exists(path))
                return path;
            else
                return GetLastExistingDirectory(path.Substring(0, path.LastIndexOf("\\")));
        }

        private void txtKMLPath_TextChanged(object sender, EventArgs e)
        {
            isKMLAnalysed = false;
        }

        #region LOG
        public void LogMessageToFile(string msg)
        {
            //Console.Error.WriteLine(msg);
            //System.IO.StreamWriter sw = System.IO.File.AppendText(
            //    homeDir + "\\log.txt");
            //try
            //{
            //    string logLine = System.String.Format(
            //        "{0:G}: {1}", System.DateTime.Now, msg);
            //    sw.WriteLine(logLine);
            //}
            //finally
            //{
            //    sw.Close();
            //}
        }
        #endregion

        private void btnErrorImages_Click(object sender, EventArgs e)
        {
            try
            {
                if (err != null && !err.Disposed)
                    err.Close();
                XmlDocument doc = new XmlDocument();
                doc.Load(Directory.GetCurrentDirectory() + @"\Error.xml");
                foreach (XmlNode node in doc.SelectNodes("/error/event[method=\"GetGoogleMapImage\"]"))
                {
                    string url = node["text"].InnerText;
                    string filename = GetFilename(url);
                    SaveImage(url, node["param"].InnerText, filename);
                }
            }
            catch (Exception ex)
            { throw; }
        }

        private string GetFilename(string url)
        {
            return "temp"+counter++.ToString();
        }
    }
}